﻿//
// This file is part of AVSReflector project
// Copyright (C) 2011 Yuan Pengfei
//

using System;
using System.Collections.Generic;
using System.Text;

namespace AVSReflector.Core
{
    public class BPicture : Picture
    {
        public uint advanced_pred_mode_disable;
        public uint no_forward_reference_flag;

        public BPicture()
        {
            picture_type = 2;
        }

        public override string[] GetInfo()
        {
            List<string> lines = new List<string>();
            lines.Add("picture_type = " + picture_type);
            lines.Add("bbv_delay = " + bbv_delay);
            lines.Add("picture_distance = " + picture_distance);
            lines.Add("progressive_frame = " + progressive_frame);
            lines.Add("picture_structure = " + picture_structure);
            lines.Add("advanced_pred_mode_disable = " + advanced_pred_mode_disable);
            lines.Add("top_field_first = " + top_field_first);
            lines.Add("repeat_first_field = " + repeat_first_field);
            lines.Add("fixed_picture_qp = " + fixed_picture_qp);
            lines.Add("picture_qp = " + picture_qp);
            lines.Add("picture_reference_flag = " + picture_reference_flag);
            lines.Add("no_forward_reference_flag = " + no_forward_reference_flag);
            lines.Add("skip_mode_flag = " + skip_mode_flag);
            lines.Add("loop_filter_disable = " + loop_filter_disable);
            lines.Add("loop_filter_parameter_flag = " + loop_filter_parameter_flag);

            return lines.ToArray();
        }

        public static BPicture Parse(BitStream s)
        {
            BPicture ret = new BPicture();
            List<INode> l = new List<INode>();

            // Parse B picture header
            ret.picture_distance = s.Read(8);
            ret.progressive_frame = s.Read(1);
            if (ret.progressive_frame == 0)
            {
                ret.picture_structure = s.Read(1);
                if (ret.picture_structure == 0)
                    ret.advanced_pred_mode_disable = s.Read(1);
            }
            else
                ret.picture_structure = 1;
            ret.top_field_first = s.Read(1);
            ret.repeat_first_field = s.Read(1);
            ret.fixed_picture_qp = s.Read(1);
            ret.picture_qp = s.Read(6);
            if(ret.picture_structure != 1)
                ret.picture_reference_flag = s.Read(1);
            ret.no_forward_reference_flag = s.Read(1);
            s.Read(3);
            ret.skip_mode_flag = s.Read(1);
            ret.loop_filter_disable = s.Read(1);
            if (ret.loop_filter_disable == 0)
            {
                ret.loop_filter_parameter_flag = s.Read(1);
                // TODO: Add loop filter parameter support
                if (ret.loop_filter_parameter_flag == 1)
                    throw new Exception("Loop filter parameter not supported!");
            }
            s.SkipToCode(0x1, 24);

            // Initialize parameters
            Parameters.PreviousQP = (int)ret.picture_qp;
            Parameters.FixedQP = ret.fixed_picture_qp;

            // Parse extension & user data
            while (true)
            {
                uint t = s.Peek(32);
                if (t == 0x1b5)
                {
                    s.Read(32);
                    INode o = null;
                    switch (s.Peek(4))
                    {
                        case 0x2:
                            s.Read(4);
                            o = SequenceDisplayExtension.Parse(s);
                            break;
                        case 0x4:
                            s.Read(4);
                            o = CopyrightExtension.Parse(s);
                            break;
                        case 0xb:
                            s.Read(4);
                            o = CameraParametersExtension.Parse(s);
                            break;
                        default:
                            s.SkipToCode(0x1, 24);
                            break;
                    }
                    if (o != null)
                        l.Add(o);
                }
                else if (t == 0x1b2)
                {
                    s.Read(32);
                    l.Add(UserData.Parse(s));
                }
                else
                    break;
            }

            // Parse picture data
            ret.BlockMatrix = new MacroBlock[Context.currentSequence.mb_height, Context.currentSequence.mb_width];
            ret.YMatrix = new int[Context.currentSequence.mb_height * 16, Context.currentSequence.mb_width * 16];
            ret.CbMatrix = new int[Context.currentSequence.mb_height * 16, Context.currentSequence.mb_width * 16];
            ret.CrMatrix = new int[Context.currentSequence.mb_height * 16, Context.currentSequence.mb_width * 16];
            Context.currentPicture = ret;
            int numslices = 0;
            while (true)
            {
                if (s.EndOfStream())
                    break;
                uint t = s.Peek(32);
                if (t >> 8 != 0x1 || (t & 0xff) >= 0xb0)
                    break;
                s.Read(24);
                ret.children = l.ToArray(); // This is not good but necessary
                l.Add(Slice.Parse(s));
                numslices++;
            }
            if (numslices <= 0)
                throw new Exception("Invalid AVS format!");
            Context.currentPicture = null;

            ret.children = l.ToArray();
            ret.GetImageBlock();
            ret.GetPictures();

            ret.Cleanup();
            return ret;
        }
    }
}
